package magneticCave;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

class PotentialLine
{
	
	

//	private ArrayList<Piece> _imaginaryPieces;
	private ArrayList<Piece> _pieces; //pieces, real and potential for this line
	private ArrayList<Piece> _imaginaryPieces; //stores only the imaginary pieces
	private int value;
	
	private int state;
	private int color;
	private boolean valueFinalized = false;
	
	private int _numColors=0;
	private int _numUnanchored=0;
	private int _numMiddles = 0;
	private int _numBlanks = 0;
	private int _whoseTurn = Leaf.NO_ONE;
	private boolean _myTurn = false;
	
	//point values, based on how far away from the end a line is.
	private static final int[] points = {
		262144, 16384, 512, 128, 16, 8, 4, 2, 1, 0
	};
	
	//bit-states for state:
	public static final int SEGMENTED=2;
	public static final int DOUBLE_ENDED=4;
	public static final int DIAGONAL=8;
	public static final int NOT_COUNTING_BLANKS=16;
	
	
	private boolean middlesUnconfirmed = true;
	private boolean mayHaveMiddles = false;
	private int numPossibleMiddles = 0;
	//num: number of pieces.
	//numInMiddle: number of pieces in the middle (used for segmented lines)
	public PotentialLine(int startState, int color, int whoseTurn)
	{

		_pieces = new ArrayList<Piece>();
		_imaginaryPieces = new ArrayList<Piece>();
		_whoseTurn = whoseTurn;
		if(color == whoseTurn)
			_myTurn = true;
		state = startState;
		this.color = color;
	}
	public PotentialLine(PotentialLine otherLine)
	{
		Init(otherLine);
	}
	protected void Init(PotentialLine otherLine)
	{
		_pieces = new ArrayList<Piece>();
		_imaginaryPieces = new ArrayList<Piece>();
		
		for(Piece o : otherLine._pieces)
			_pieces.add(o); //pieces, real and potential for this line
		
		for(Piece o : otherLine._imaginaryPieces)
			_imaginaryPieces.add(o);
		
		value = otherLine.value;
		
		state = otherLine.state;
		color = otherLine.color;
		valueFinalized = otherLine.valueFinalized;
		
		_numColors = otherLine._numColors;
		_numUnanchored = otherLine._numUnanchored;
		_numMiddles = otherLine._numMiddles;
		_numBlanks = otherLine._numBlanks;
		
		_myTurn = otherLine._myTurn;
		_whoseTurn = otherLine._whoseTurn;
		//bit-states for state:
		middlesUnconfirmed = otherLine.middlesUnconfirmed;
		mayHaveMiddles = otherLine.mayHaveMiddles;
		numPossibleMiddles = otherLine.numPossibleMiddles;
	}
	
	public void AddState(int newState)
	{
		state |= newState;
	}
	
	public void SetColor(int newColor)
	{
		color = newColor;
		if(color == _whoseTurn)
			_myTurn = true;
		else
			_myTurn = false;
	}
	
	public void Clear()
	{
		state = 0;
		value = 0;
		_pieces.clear();
	}
	
	
	//don't count any more blanks that get added
	public void StopCountingBlanks()
	{
		state |= NOT_COUNTING_BLANKS;
	}
	
	public void AddPiece(int nx, int ny, int pieceState, int pieceColor, int[] grid)
	{
		valueFinalized = false;
		if(pieceColor != Leaf.BLACK && pieceColor != Leaf.WHITE)
			pieceState |= Piece.IMAGINARY;
		else 
		{
			color = pieceColor;
			if(color == _whoseTurn)
				_myTurn = true;
		}
		//check to see if the piece is anchored
		if((pieceState & Piece.IMAGINARY) == Piece.IMAGINARY)
		{
			if((state & NOT_COUNTING_BLANKS) != NOT_COUNTING_BLANKS)
			{
				_numBlanks++;
				
				
				if((state & PotentialLine.DIAGONAL) == PotentialLine.DIAGONAL)
				{
					//check to the sides to see if its anchored
					if( nx == 0 || nx == Leaf.COLS - 1 ||
						(nx -1 >= 0 && grid[(ny * Leaf.ROWS) + nx-1] != 0) || //check left
						(ny - 1 >= 0 && grid[((ny-1) * Leaf.ROWS) + nx] != 0) || //check up
						(nx+1 < Leaf.COLS && grid[(ny * Leaf.ROWS) + nx+1] != 0) || //check right
						((ny+1) < Leaf.ROWS && grid[((ny+1) * Leaf.ROWS) + nx] != 0))//check down
					{
						pieceState |= Piece.ANCHORED; 
					}
					else
						_numUnanchored++;
				}
				else
					pieceState |= Piece.ANCHORED; //pieces in non-diagonal lines are automatically anchored
			}
			
	
		}
		else
			pieceState |= Piece.ANCHORED; //real pieces are automatically anchored

		//figure out whether we have middles or not - affects scoring
		if(middlesUnconfirmed && _numColors >= 1 && (pieceState & Piece.IMAGINARY) == Piece.IMAGINARY)
		{
			mayHaveMiddles = true;
			numPossibleMiddles++;
		}
		else if(mayHaveMiddles && (pieceState & Piece.IMAGINARY) != Piece.IMAGINARY)
		{
			mayHaveMiddles = false;
			_numMiddles = numPossibleMiddles;
			middlesUnconfirmed = false;
		}
		
		Piece piece = new Piece(nx, ny, pieceState);
		_pieces.add(piece);
		
		if((pieceState & Piece.IMAGINARY) == Piece.IMAGINARY)
		{
			_imaginaryPieces.add(piece);
		}		
	}
	
	public void AddPiece(Piece piece)
	{
		_pieces.add(piece);
		if((piece.state & Piece.IMAGINARY) == Piece.IMAGINARY)
		{
			if((state & NOT_COUNTING_BLANKS) != NOT_COUNTING_BLANKS)
			{
				_numBlanks++;
				
				if((piece.state & Piece.ANCHORED) != Piece.ANCHORED)
					_numUnanchored++;
				
			}
		}
	}
	
	
	//set the value according to our rules
	private void FinalizeValue()
	{
		valueFinalized = true;
		if(_pieces.size() <= 0)
		{
			value = 0;
			return;
		}
		
		//the score is inversely proportional to how far away you are from making this into a winning line.
		int howFar = _numBlanks + _numUnanchored;  //unanchored diagonals count for 2
		
		
		//figure out if we're double-ended or not
		if(_numMiddles <= 0 && (_pieces.get(0).state & Piece.IMAGINARY)== Piece.IMAGINARY && 
				(_pieces.get(_pieces.size() -1).state & Piece.IMAGINARY) == Piece.IMAGINARY)
		{
			state |= DOUBLE_ENDED;
			
			if((_myTurn && howFar == 2) || howFar == 1)
			{
			
				howFar = 0;
			}
			else
			{
				howFar -= 1;
				if(howFar < 0)
					howFar = 0;
			}
			

		}
		if(howFar >= 10)
		{
			System.out.println("Warning: Line is farther than 9 away, fixing.");
			howFar = 9;
		}
		value = points[howFar];
	}
	public int size()
	{
		return _pieces.size();
	}
	
	public int GetValue()
	{
		if(!valueFinalized)
			FinalizeValue();
		return value;
	}
	
	public int GetColor()
	{
		return color;
	}
	 
	public ArrayList<Piece> GetImaginaryPieces()
	{
		return _imaginaryPieces;
	}
	public ArrayList<Piece> GetPieces()
	{
		return _pieces;
	}
	//returns 0, 1, or 2
	//0: don't delete any lines
	//1: delete this line (the one calling the function)
	//2: delete the line being passed in
	public int Collide(PotentialLine line)
	{
		ArrayList<Piece> pieceList = line.GetImaginaryPieces();
		//loop through imaginary pieces
		for(Iterator<Piece> p = _imaginaryPieces.iterator(); p.hasNext(); )
		{
			Piece myPiece = p.next();
			for(Iterator o = pieceList.iterator(); o.hasNext();)
			{				
				Piece otherPiece = (Piece)o.next();
				
				//check for collision
				if(myPiece.x == otherPiece.x && myPiece.y == otherPiece.y)
				{	
					
					//we have to "remove" this spot (blank piece), counting it as blocked for ONE
					//of these lines, since they intersect.
					//First we have to figure out which outcome is preferable for scoring purposes:
					//which outcome results in the best score possible for the player
					otherPiece.AddState(Piece.BLOCKED);
					
					PotentialLine otherLineCopy = new PotentialLine(line); //backup the other line
				
					boolean keepLine = otherLineCopy.ReEvaluate();
					int newVal = otherLineCopy.GetValue() + GetValue();
					otherPiece.RemoveState(Piece.BLOCKED);
					otherLineCopy = new PotentialLine(line);
					
					myPiece.AddState(Piece.BLOCKED);
					
					PotentialLine thisLineCopy = new PotentialLine(this);
					boolean keepThisLine = thisLineCopy.ReEvaluate();
					
					if(otherLineCopy.GetValue() + thisLineCopy.GetValue() > newVal)
					{
						if(!keepThisLine)
							return 1;
					}
					else
					{
						myPiece.RemoveState(Piece.BLOCKED);
						//ReEvaluate();	//remove other line
						if(!keepLine)
							return 2;				
					}
						
					
					
					
					
				}
				
			}
		}
		return 0;
	}
	
	
	//re-evaluates the value of the line, returns false if the line is no longer valid.
	//called as a result of Collide()
	public boolean ReEvaluate()
	{	
		Piece p;
		//Iterator toDel=null;
		boolean lastWasBlocked = false;
		boolean lastWasColor = false;
		int numPieces = 0; //number of pieces. If blocked, and numContiguous < 5, numPieces 
						    //goes down to 0
		
		//Iterator itBeg = _pieces.descendingIterator();
		ArrayList<Piece> pieceList = new ArrayList<Piece>();
		int lineflag = 0;
		
		if((state & DIAGONAL) == DIAGONAL)
			lineflag |= DIAGONAL;
			
		PotentialLine bestLine = new PotentialLine(lineflag, color, _whoseTurn);
		PotentialLine potLine = new PotentialLine(lineflag, color, _whoseTurn);
		//loop through all possible combinations of this line until we find the best row of 5
		//then add all the white space around it (so we can evaluate double-endedness later)
		boolean done = false;
		int startPoint = 0;
		
		
		for(int i=0; i < _pieces.size(); i++)
		{
			p = (Piece)_pieces.get(i);
			
			//we encountered the blocked one.
			if((p.state & Piece.BLOCKED) == Piece.BLOCKED)
			{
				
				if(lastWasColor)
					state &= ~(DOUBLE_ENDED);
					
				lastWasBlocked = true;
				lastWasColor = false;
				
				//if we're exactly at 5 when we see a block, we can stop. There are no better combinations
				if(potLine.size() == 5)
				{
					GetSurroundingBlanks(potLine, _pieces, startPoint, i+1);
					if(potLine.GetValue() > bestLine.GetValue())
					{
						bestLine = potLine;
					}
				}
				//we've seen a block, lets start over
				potLine = new PotentialLine(lineflag, color, _whoseTurn);
				continue;
			}
			
			potLine.AddPiece(p); //add the piece to our line (blank or color)
			
			if((p.state & Piece.IMAGINARY) != Piece.IMAGINARY) //ran into a color piece
			{
				if(lastWasBlocked)  //if a block is no longer valid, then we're not double-ended
					state &= ~(DOUBLE_ENDED);
				
				lastWasColor = true;
				lastWasBlocked = false;
			}
			else //blank piece
			{
				lastWasColor = false;
				lastWasBlocked =false;
			}
			//if we've reached 5, go forward and get all the blanks, then go back and do the same
			if(potLine.size() == 5)
			{
				
				GetSurroundingBlanks(potLine, _pieces, startPoint, i+1);
				i = ++startPoint;
			
				
				if(potLine.GetValue() > bestLine.GetValue())
					bestLine = potLine;
				potLine = new PotentialLine(lineflag, color, _whoseTurn);
			}
		}

		
		
		Init(bestLine);
		FinalizeValue();
		
		return value != 0;
	
	}
	public static void GetSurroundingBlanks(PotentialLine potLine, final ArrayList<Piece> _pieces, int startPoint, int endPoint)
	{
		boolean search = true;
		potLine.StopCountingBlanks();
		//go back and get the pieces
		for(int j = startPoint; j > 0 && search; j--)
		{
			Piece bp = (Piece)_pieces.get(j);
			//pick up blank pieces
			if((bp.state & Piece.IMAGINARY) == Piece.IMAGINARY && 
					(bp.state & Piece.BLOCKED) != Piece.BLOCKED)
			{
				potLine.AddPiece(bp);
			}
			else
			{
				//we've seen a block, go back to where we were before and pick up the forward blanks
				search = false;
			}
		}
		//search forward
		search = true;
		for(int j = endPoint; j < _pieces.size() && search; j++)
		{
			Piece fp = (Piece)_pieces.get(j);
			if((fp.state & Piece.IMAGINARY) == Piece.IMAGINARY && 
					(fp.state & Piece.BLOCKED) != Piece.BLOCKED)
			{
				potLine.AddPiece(fp);
			}
			else
			{
				//we've seen a block or a real piece, stop here
				search = false;
			}
		}
	}
	
	//endX, endY: The end of the line, where we search forward from.
	public static void GetSurroundingBlanks(PotentialLine potLine, final int[] grid, int endX, int endY, int incRow, int incCol)
	{
		potLine.StopCountingBlanks();
		
		//go back and get the pieces
		incRow *= -1;
		incCol *= -1;
		
		int col = endX + (incCol * 4); 
		int row = endY + (incRow * 4);

		int ops = 0; //number of operations completed (fwd and rev)
		while(ops < 2)
		{
		
			int piece = grid[(row * Leaf.ROWS) + col];
			if(piece == 0)
				potLine.AddPiece(col, row, Piece.IMAGINARY, piece, grid);			
			else
			{
				//we've seen a block, go back to where we were before and pick up the forward blanks
				ops++;
			
				
				incRow *= -1;
				incCol *= -1;
				row = endY;
				col = endX;
			}	
			row += incRow;
			col += incCol;
			if(row >= Leaf.ROWS || row < 0 || col >= Leaf.COLS || col < 0)
				ops = 2;
		}
		
	
	}

}
